// Ofri Keidar 302933981 group 05

package model.apartment;

import java.util.HashMap;
import java.util.Map;

/**
 * Class to create apartments, using Factory Pattern.
 * In order to create an apartment, use create method and send a type string and map of parameters
 * or each parameter separately.
 * Uses the Parameter-Parser interface for map's keys
 */
public class ApartmentFactory {
	
	/**
	 * Interface of factory. Each factory of apartment's derived type should implement it
	 */
	private interface ApartmentFac {
		public Apartment create(Map<String, String> parameters, int bldID, String address);
		public Apartment create(int floor, double roomNumber, double size, double extraUnit, String residentName, boolean separateEntrace, int apartmentNum, int bldID, String address);
	}
	
	/** 
	 * Class used to create a flat with given parameters
	 */
	private class FlatFactory implements ApartmentFac {
		
		/**
		 * Parses parameters out of given map, and returns a new flat with 
		 * given values
		 */
		@Override
		public Apartment create(Map<String, String> parameters, int bldID, String address) {
			
			// parse flat's parameters out of parameters string
			int floor = Integer.parseInt(parameters.get(ParameterParser.FLOOR));
			double roomNumber = Double.parseDouble(parameters.get(ParameterParser.ROOM_NUMBER));
			double size = Double.parseDouble(parameters.get(ParameterParser.SIZE));
			double warehouse = Double.parseDouble(parameters.get(ParameterParser.STORAGE_AREA));
			int apartmentNum = Integer.parseInt(parameters.get(ParameterParser.APARTMENT_NUMBER));
			
			// check if received resident name
			String residentName = "";
			if (parameters.containsKey(ParameterParser.RESIDENT_NAME)) {
				residentName = parameters.get(ParameterParser.RESIDENT_NAME);	
			}
			
			// create a new flat with given parameters and return it
			return new Flat(floor, roomNumber, size, warehouse, residentName, apartmentNum, bldID, address);
		}
		
		/**
		 * Returns a new flat with given values
		 */
		@Override
		public Apartment create(int floor, double roomNumber, double size, double extraUnit, String residentName, boolean separateEntrace, int apartmentNum, int bldID, String address) {
			return new Flat(floor, roomNumber, size, extraUnit, residentName, apartmentNum, bldID, address);
		}
	}
	
	/** 
	 * Class used to create a garden apartment with given parameters
	 */
	private class GardenApartmentFactory implements ApartmentFac {
		
		/**
		 * Parses parameters out of given map, and returns a new garden apartment with 
		 * given values
		 */
		@Override
		public Apartment create(Map<String, String> parameters, int bldID, String address) {
			
			// parse flat's parameters out of parameters string			
			double roomNumber = Double.parseDouble(parameters.get(ParameterParser.ROOM_NUMBER));
			double size = Double.parseDouble(parameters.get(ParameterParser.SIZE));
			double gardenArea = Double.parseDouble(parameters.get(ParameterParser.GARDEN_AREA));
			boolean sepEntrance = Boolean.parseBoolean(parameters.get(ParameterParser.SEPARATE_ENTRANCE));
			int apartmentNum = Integer.parseInt(parameters.get(ParameterParser.APARTMENT_NUMBER));
						
			// check if received resident name
			String residentName = "";
			if (parameters.containsKey(ParameterParser.RESIDENT_NAME)) {
				residentName = parameters.get(ParameterParser.RESIDENT_NAME);
			}
			 			
			// create a new garden apartment with given parameters and return it
			return new GardenApartment(roomNumber, size, gardenArea, residentName, sepEntrance, apartmentNum, bldID, address);						
		}
		
		/**
		 * Returns a new garden apartment with given values
		 */
		@Override
		public Apartment create(int floor, double roomNumber, double size, double extraUnit, String residentName, boolean separateEntrace, int apartmentNum, int bldID, String address) {
			return new GardenApartment(roomNumber, size, extraUnit, residentName, separateEntrace, apartmentNum, bldID, address);
		}
	}
	
	/** 
	 * Class used to create a penthouse with given parameters
	 */
	private class PenthouseFactory implements ApartmentFac {
		
		/**
		 * Parses parameters out of given map, and returns a new penthouse with 
		 * given values
		 */
		@Override
		public Apartment create(Map<String, String> parameters, int bldID, String address) {
			
			// parse flat's parameters out of parameters string
			int floor = Integer.parseInt(parameters.get(ParameterParser.FLOOR));
			double roomNumber = Double.parseDouble(parameters.get(ParameterParser.ROOM_NUMBER));
			double size = Double.parseDouble(parameters.get(ParameterParser.SIZE));
			double balcony = Double.parseDouble(parameters.get(ParameterParser.BALCONY_AREA));
			int apartmentNum = Integer.parseInt(parameters.get(ParameterParser.APARTMENT_NUMBER));
			
			// check if received resident name
			String residentName = "";
			if (parameters.containsKey(ParameterParser.RESIDENT_NAME)) {
				residentName = parameters.get(ParameterParser.RESIDENT_NAME);	
			}
			
			// create a new flat with given parameters and return it
			return new Penthouse(floor, roomNumber, size, balcony, residentName, apartmentNum, bldID, address);
		}
		
		/**
		 * Returns a new penthouse with given values
		 */
		@Override
		public Apartment create(int floor, double roomNumber, double size, double extraUnit, String residentName, boolean separateEntrace, int apartmentNum, int bldID, String address) {
			return new Penthouse(floor, roomNumber, size, extraUnit, residentName, apartmentNum, bldID, address);
		}
	}
		
	private HashMap<String, ApartmentFac> apartmentFactory; // used to activate create method of correct type
	
	/**
	 * Constructor- creates a new apartment factory
	 */
	public ApartmentFactory() {
		
		// allocate a new map of apartment factories
		apartmentFactory = new HashMap<String, ApartmentFactory.ApartmentFac>();
		// set flat factory
		apartmentFactory.put(ParameterParser.FLAT, new FlatFactory());
		// set garden apartment factory
		apartmentFactory.put(ParameterParser.GARDEN_APARTMENT, new GardenApartmentFactory());
		// set penthouse factory
		apartmentFactory.put(ParameterParser.PENTHOUSE, new PenthouseFactory());
	}
	
	/**
	 * Gets apartment type and map of parameters and creates a new apartment accordingly
	 * @param type- type for new apartment
	 * @param parameters- map of parameters for new apartment
	 * @param apartmentNum- apartment's number in building
	 * @param bldID- apartment's building ID
	 * @param address- apartment's building address
	 * @return new apartment of given type with given values
	 */
	public Apartment create(String type, Map<String, String> parameters, int bldID, String address) {
		return apartmentFactory.get(type).create(parameters, bldID, address);
	}
	
	/**
	 * Gets apartment's parameters and creates a new apartment accordingly
	 * @param type- type for new apartment
	 * @param parameters- map of parameters for new apartment
	 * @param apartmentNum- apartment's number in building
	 * @param bldID- apartment's building ID
	 * @param address- apartment's building address
	 * @return new apartment of given type with given values
	 */
	public Apartment create(String type, int floor, double roomNumber, double size, double extraUnit, String residentName, boolean separateEntrace, int apartmentNum, int bldID, String address) {
		return apartmentFactory.get(type).create(floor, roomNumber, size, extraUnit, residentName, separateEntrace, apartmentNum, bldID, address);
	}
}